{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "896fcdd8",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 预处理数据\n",
    "\n",
    "# Load origin data\n",
    "import sys\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "\n",
    "data_file = './rawdata.csv'  # 学生1-6年的近视数据\n",
    "data = pd.read_csv(data_file, encoding ='utf-8')# 有中文用gbk，字符集用utf-8\n",
    "\n",
    "# 丢弃前4列以及后面useless的数据，切勿重复执行，会一直裁剪数据\n",
    "data = data.iloc[:, 4:28]# 前的：代表行，后面的代表列，：前后是代表从几行（列）到几行（列）\n",
    "\n",
    "# 提取每一年的数据重新排列\n",
    "data = data.loc[:,['1SE','1eye','1 0.5(K1+K2)','1 logMAR','2SE','2eye','2 0.5(K1+K2)','2 logMAR'\\\n",
    "                    ,'3SE','3eye','3 0.5(K1+K2)','3 logMAR','4SE','4eye','4 0.5(K1+K2)','4 logMAR'\\\n",
    "                   ,'5SE','5eye','5 0.5(K1+K2)','5 logMAR','6SE','6eye','6 0.5(K1+K2)','6 logMAR']]\n",
    "\n",
    "# 提取前66行数据作为训练数据，放入train_data.csv文件中\n",
    "train_data = data.iloc[0:66, :]\n",
    "train_data.to_csv('./train_data.csv')\n",
    "\n",
    "# 提取后10行数据作为测试数据，放入test_data.csv文件中\n",
    "test_data = data.iloc[66:76, :]\n",
    "test_data.to_csv('./test_data.csv')\n",
    "\n",
    "# 注意原始数据中有一个数据是空值，24行5eye，需要进行填充，我选择手动填充\n",
    "# 不然后续算loss值会变成nan值，因为前面标准化的值就不正确了，成nan值了\n",
    "\n",
    "# 加载训练数据\n",
    "#train_data = pd.read_csv('./train_data.csv')\n",
    "\n",
    "# 把dataframe转换成numpy数组，train_data此时是65*24，test_data是10*24\n",
    "train_data = train_data.to_numpy()\n",
    "test_data = test_data.to_numpy()\n",
    "\n",
    "# 提取特征，放入x，y\n",
    "x = np.empty([66, 20], dtype = float)\n",
    "y = np.empty([66, 1], dtype = float)\n",
    "for people in range(66):\n",
    "    x[people, :] = train_data[people, 0:20]\n",
    "    y[people, :] = train_data[people, 23]\n",
    "    \n",
    "# 对x标准化# 求平均\n",
    "mean_x = np.mean(x, axis = 0) # 计算每一列的平均值（axis = 0），axis = 1则求每一行的平均值\n",
    "std_x = np.std(x, axis = 0) # 计算标准差\n",
    "\n",
    "for i in range(len(x)):\n",
    "    for j in range(len(x[0])):\n",
    "        if std_x[j] != 0:\n",
    "            x[i][j] = (x[i][j] - mean_x[j]) / std_x[j]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "03626a9f",
   "metadata": {},
   "outputs": [],
   "source": [
    "#线性回归Linear regression\n",
    "\n",
    "import numpy as np\n",
    "import torch\n",
    "import torchvision\n",
    "from torch.utils import data\n",
    "from d2l import torch as d2l\n",
    "\n",
    "# 获取数据\n",
    "# 注意，因为下面的load_array函数使用时需要的是tensor，所以需要将numpy转化为tensor（张量）\n",
    "\n",
    "x, y = torch.from_numpy(x), torch.from_numpy(y)\n",
    "x, y = x.to(torch.float32), y.to(torch.float32) # 后面报错有double数据，在这先转换成float\n",
    "features, labels = x, y\n",
    "#features, labels"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "782587fe",
   "metadata": {},
   "source": [
    "### TensorDataset\n",
    "* 对给定的tensor数据(样本和标签)，将它们包装成dataset。注意，如果是numpy的array，或者Pandas的DataFrame需要先转换成Tensor。\n",
    "\n",
    "### DataLoader\n",
    "* 数据加载器，组合数据集和采样器，并在数据集上提供单进程或多进程迭代器。它可以对我们上面所说的数据集Dataset作进一步的设置。\n",
    "\n",
    " * dataset (Dataset) – 加载数据的数据集。\n",
    " * batch_size (int, optional) – 每个batch加载多少个样本(默认: 1)。\n",
    " * shuffle (bool, optional) – 设置为True时会在每个epoch重新打乱数据(默认: true).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "10605a6a",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[tensor([[ 0.2750, -0.7003,  1.4880, -0.6361, -1.4206, -0.5817,  0.5924, -0.4214,\n",
       "          -0.2237, -0.4309,  0.8692, -0.7351, -0.0466, -0.4116,  1.4817,  0.1415,\n",
       "          -0.2278, -0.2250,  1.4089, -0.0667],\n",
       "         [ 2.3689, -0.8750,  0.2421,  1.8743,  1.1904, -0.9648, -0.1263, -0.4214,\n",
       "           0.9048, -0.8492,  0.1487, -0.7351,  1.0564, -1.0321,  0.0227, -0.8268,\n",
       "           1.3180, -1.0011, -0.0512, -0.9944],\n",
       "         [ 0.0423,  1.3818, -1.3153,  0.4605, -0.3524,  1.2965, -1.0606, -0.4214,\n",
       "          -0.6939,  1.3742, -0.7519,  0.9141, -0.9131,  1.1898, -1.1236,  0.8550,\n",
       "          -0.7899,  1.5398, -1.1984,  1.1597],\n",
       "         [ 0.5076, -0.0287,  0.2421, -0.6361,  0.1223, -0.1863,  1.7423, -0.4214,\n",
       "          -1.2581, -0.1668,  0.1487,  0.9141, -0.4405, -0.2014,  0.1270,  0.8550,\n",
       "          -0.7197,  1.2102,  0.2617,  1.1597],\n",
       "         [-0.8883, -0.4451, -0.0694, -0.6361, -1.3019, -0.6806,  2.0297, -0.4214,\n",
       "          -0.4118, -0.7391, -0.3916, -0.7351,  0.8200, -0.9821, -0.0815, -0.8268,\n",
       "           0.9667, -1.2031, -0.0512, -0.9944],\n",
       "         [-1.1210,  0.3206,  0.1382, -0.6361,  0.4783,  0.1103, -0.0544, -0.4214,\n",
       "          -0.1297,  0.0864,  0.0587, -0.7351,  0.3474,  0.0789,  0.0227, -0.8268,\n",
       "           0.4748,  0.2534,  0.1574, -0.9944]]),\n",
       " tensor([[0.6021],\n",
       "         [0.0000],\n",
       "         [0.6021],\n",
       "         [1.3010],\n",
       "         [0.0000],\n",
       "         [0.2218]])]"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 读取数据集\n",
    "\n",
    "def load_array(data_arrays, batch_size, is_train=True):  #@save\n",
    "    \"\"\"构造一个PyTorch数据迭代器。\"\"\"\n",
    "    dataset = data.TensorDataset(*data_arrays)\n",
    "    return data.DataLoader(dataset, batch_size, shuffle=is_train)\n",
    "\n",
    "batch_size = 6 # 30的loss值最低\n",
    "data_iter = load_array((features, labels), batch_size)\n",
    "\n",
    "next(iter(data_iter))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "05dd1685",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Sequential(\n",
       "  (0): Linear(in_features=20, out_features=1, bias=True)\n",
       ")"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# `nn` 是神经网络的缩写\n",
    "from torch import nn\n",
    "\n",
    "net = nn.Sequential(nn.Linear(20, 1))\n",
    "net"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "bb58b377",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([0.])"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 初始化模型参数\n",
    "net[0].weight.data.normal_(0, 0.01)\n",
    "net[0].bias.data.fill_(0)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "bc22f5f3",
   "metadata": {},
   "outputs": [],
   "source": [
    "# 定义损失函数，MSELoss类，平方范数\n",
    "loss = nn.MSELoss()\n",
    "#loss"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "271a8376",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "SGD (\n",
       "Parameter Group 0\n",
       "    dampening: 0\n",
       "    lr: 0.001\n",
       "    momentum: 0\n",
       "    nesterov: False\n",
       "    weight_decay: 0\n",
       ")"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# 定义优化算法，小批量随机梯度下降算法\n",
    "# 实例化SGD实例\n",
    "trainer = torch.optim.SGD(net.parameters(), lr=0.001)\n",
    "trainer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "3da711a8",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "epoch 1, loss 0.240433\n",
      "epoch 2, loss 0.223730\n",
      "epoch 3, loss 0.210190\n",
      "epoch 4, loss 0.198648\n",
      "epoch 5, loss 0.188694\n",
      "epoch 6, loss 0.180040\n",
      "epoch 7, loss 0.172285\n",
      "epoch 8, loss 0.165192\n",
      "epoch 9, loss 0.158685\n",
      "epoch 10, loss 0.152721\n",
      "epoch 11, loss 0.147149\n",
      "epoch 12, loss 0.141923\n",
      "epoch 13, loss 0.136988\n",
      "epoch 14, loss 0.132327\n",
      "epoch 15, loss 0.127938\n",
      "epoch 16, loss 0.123767\n",
      "epoch 17, loss 0.119806\n",
      "epoch 18, loss 0.116037\n",
      "epoch 19, loss 0.112447\n",
      "epoch 20, loss 0.109031\n",
      "epoch 21, loss 0.105768\n",
      "epoch 22, loss 0.102648\n",
      "epoch 23, loss 0.099667\n",
      "epoch 24, loss 0.096829\n",
      "epoch 25, loss 0.094124\n",
      "epoch 26, loss 0.091529\n",
      "epoch 27, loss 0.089046\n",
      "epoch 28, loss 0.086675\n",
      "epoch 29, loss 0.084402\n",
      "epoch 30, loss 0.082227\n",
      "epoch 31, loss 0.080147\n",
      "epoch 32, loss 0.078164\n",
      "epoch 33, loss 0.076261\n",
      "epoch 34, loss 0.074442\n",
      "epoch 35, loss 0.072698\n",
      "epoch 36, loss 0.071024\n",
      "epoch 37, loss 0.069422\n",
      "epoch 38, loss 0.067888\n",
      "epoch 39, loss 0.066421\n",
      "epoch 40, loss 0.065014\n",
      "epoch 41, loss 0.063671\n",
      "epoch 42, loss 0.062385\n",
      "epoch 43, loss 0.061152\n",
      "epoch 44, loss 0.059971\n",
      "epoch 45, loss 0.058835\n",
      "epoch 46, loss 0.057752\n",
      "epoch 47, loss 0.056714\n",
      "epoch 48, loss 0.055719\n",
      "epoch 49, loss 0.054766\n",
      "epoch 50, loss 0.053849\n",
      "epoch 51, loss 0.052975\n",
      "epoch 52, loss 0.052134\n",
      "epoch 53, loss 0.051326\n",
      "epoch 54, loss 0.050554\n",
      "epoch 55, loss 0.049812\n",
      "epoch 56, loss 0.049103\n",
      "epoch 57, loss 0.048421\n",
      "epoch 58, loss 0.047767\n",
      "epoch 59, loss 0.047141\n",
      "epoch 60, loss 0.046541\n",
      "epoch 61, loss 0.045966\n",
      "epoch 62, loss 0.045414\n",
      "epoch 63, loss 0.044884\n",
      "epoch 64, loss 0.044377\n",
      "epoch 65, loss 0.043888\n",
      "epoch 66, loss 0.043420\n",
      "epoch 67, loss 0.042969\n",
      "epoch 68, loss 0.042540\n",
      "epoch 69, loss 0.042128\n",
      "epoch 70, loss 0.041731\n",
      "epoch 71, loss 0.041349\n",
      "epoch 72, loss 0.040983\n",
      "epoch 73, loss 0.040631\n",
      "epoch 74, loss 0.040292\n",
      "epoch 75, loss 0.039968\n",
      "epoch 76, loss 0.039656\n",
      "epoch 77, loss 0.039355\n",
      "epoch 78, loss 0.039066\n",
      "epoch 79, loss 0.038791\n",
      "epoch 80, loss 0.038523\n",
      "epoch 81, loss 0.038267\n",
      "epoch 82, loss 0.038020\n",
      "epoch 83, loss 0.037782\n",
      "epoch 84, loss 0.037554\n",
      "epoch 85, loss 0.037334\n",
      "epoch 86, loss 0.037121\n",
      "epoch 87, loss 0.036918\n",
      "epoch 88, loss 0.036722\n",
      "epoch 89, loss 0.036534\n",
      "epoch 90, loss 0.036352\n",
      "epoch 91, loss 0.036177\n",
      "epoch 92, loss 0.036008\n",
      "epoch 93, loss 0.035845\n",
      "epoch 94, loss 0.035687\n",
      "epoch 95, loss 0.035537\n",
      "epoch 96, loss 0.035390\n",
      "epoch 97, loss 0.035251\n",
      "epoch 98, loss 0.035116\n",
      "epoch 99, loss 0.034985\n",
      "epoch 100, loss 0.034860\n"
     ]
    }
   ],
   "source": [
    "# 训练模型\n",
    "num_epochs = 100\n",
    "for epoch in range(num_epochs):\n",
    "    for x, y in data_iter:\n",
    "        l = loss(net(x), y)\n",
    "        trainer.zero_grad()\n",
    "        l.backward()\n",
    "        trainer.step()\n",
    "    l = loss(net(features), labels)\n",
    "    #if(epoch%100==0):\n",
    "    #    print(f'epoch {epoch}, loss {l:f}')\n",
    "    print(f'epoch {epoch + 1}, loss {l:f}')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "dd8f88d7",
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "(tensor([[ 0.0545,  0.0059,  0.0055, -0.0094,  0.0077, -0.0082, -0.0052, -0.0004,\n",
       "          -0.0197,  0.0100,  0.0206, -0.0096, -0.0064,  0.0283,  0.0063,  0.0616,\n",
       "          -0.0769,  0.0307,  0.0098,  0.1065]]),\n",
       " tensor([0.3553]))"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "w = net[0].weight.data\n",
    "b = net[0].bias.data\n",
    "w, b"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2076aed6",
   "metadata": {},
   "source": [
    "(tensor([[ 0.0171,  0.0074,  0.0184,  0.0019, -0.0013,  0.0082,  0.0019,  0.0024,\n",
    "          -0.0252,  0.0274, -0.0036,  0.0137, -0.0334,  0.0213,  0.0063,  0.0450,\n",
    "          -0.0391,  0.0118,  0.0030,  0.0491]]),\n",
    " tensor([0.1079]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2b44a3b4",
   "metadata": {
    "scrolled": true
   },
   "outputs": [],
   "source": [
    "# 将test数据也变成和训练数据一样的形式，取前五年的数据\n",
    "test_x = np.empty([10, 20], dtype = float)\n",
    "for t_people in range(10):\n",
    "    test_x[t_people, :] = test_data[t_people, 0:20]\n",
    "    \n",
    "# 数据标准化  \n",
    "for i in range(len(test_x)):\n",
    "    for j in range(len(test_x[0])):\n",
    "        if std_x[j] != 0:\n",
    "            test_x[i][j] = (test_x[i][j] - mean_x[j]) / std_x[j]\n",
    "\n",
    "# 将numpy变为tensor       \n",
    "test_x = torch.from_numpy(test_x)\n",
    "test_x = test_x.to(torch.float32)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "261736d4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor([[0.3085],\n",
       "        [0.1313],\n",
       "        [0.0250],\n",
       "        [0.2642],\n",
       "        [0.5200],\n",
       "        [0.3169],\n",
       "        [0.4567],\n",
       "        [0.8294],\n",
       "        [0.6746],\n",
       "        [0.6398]])"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "ans_y = torch.matmul(test_x,w.T) + b\n",
    "ans_y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "2f929f29",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 1 Axes>"
      ]
     },
     "metadata": {
      "needs_background": "light"
     },
     "output_type": "display_data"
    }
   ],
   "source": [
    "# 画出预测值和实际值的图像\n",
    "real_y = test_data[:, 23]\n",
    "#real_y\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "fig = plt.figure()\n",
    "\n",
    "from itertools import chain\n",
    "x = np.arange(1, 11, 1)\n",
    "\n",
    "##使用库函数,把二维数组变为一维数组，方便画图\n",
    "y1 = list(chain.from_iterable(ans_y))\n",
    "\n",
    "plt.plot(x, y1, label='ans_y', marker = \"o\", color='r', linestyle='-')\n",
    "plt.plot(x, real_y, label='real_y', marker = \"x\", color='b', linestyle='-.')\n",
    "plt.xlabel(\"x\")\n",
    "plt.ylabel(\"y\")\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e0417658",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
